package model.dao;

import it.mrcrack.exception.DataFormatException;
import it.mrcrack.exception.DataNotFoundException;
import it.mrcrack.model.DBDAO;

import java.sql.SQLException;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Vector;

import model.AcquisitionsFactory;
import model.bean.MancatoFunzionamento;
import model.bean.Misura;
import model.util.Connessione;

public class MancatiFunzionamentiDBDAO extends DBDAO implements AcquisitionsFactory<MancatoFunzionamento>{
	/**
	 * Costruttore
	 * @param conn Connessione al db
	 * @throws DataFormatException la connessione passata non puo' essere null
	 */
	public MancatiFunzionamentiDBDAO(Connessione conn) throws DataFormatException{
		super(conn);
	}

	/**
	 * Crea un vector contenente tutti i mancati funzionamenti presenti nel database
	 * @return vector di mancati funzionamenti
	 * @throws SQLException
	 */
	@Override
	public List<MancatoFunzionamento> get() throws SQLException {
		Vector<MancatoFunzionamento> vm=new Vector<MancatoFunzionamento>();
		try {
			st=conn.getConn().createStatement();
			rs=st.executeQuery("SELECT * FROM MancatiFunzionamenti ORDER BY Ora;");
			while(rs.next()){
				MancatoFunzionamento m=new MancatoFunzionamento();
				GregorianCalendar gc=new GregorianCalendar();
				gc.setTime(new Date(rs.getLong("Ora")));
				try {
					m.setOra(gc);
					m.setDurata(rs.getLong("Durata"));
					m.setErrore(rs.getString("Errore"));
					vm.add(m);
				} catch (DataFormatException e) {
					e.printStackTrace();
				}
			}
		} finally {
			rs.close();
			ps.close();
			rs=null;
			ps=null;
		}
		return vm;
	}

	/**
	 * Crea un vector contenente tutti i mancati funzionamenti del giorno passato
	 * @param data data ricerca
	 * @return vector di mancati funzionamenti
	 * @throws SQLException
	 */
	@Override
	public List<MancatoFunzionamento> get(GregorianCalendar data) throws SQLException{
		if(data==null)
			throw new NullPointerException("La data passata non puo' essere null");
		GregorianCalendar gci, gcf;
		gci=(GregorianCalendar)data.clone();
		gci.set(GregorianCalendar.HOUR_OF_DAY, 0);
		gci.set(GregorianCalendar.MINUTE, 0);
		gci.set(GregorianCalendar.SECOND, 0);
		gci.set(GregorianCalendar.MILLISECOND, 0);
		gcf=(GregorianCalendar)data.clone();
		gcf.set(GregorianCalendar.HOUR_OF_DAY, 23);
		gcf.set(GregorianCalendar.MINUTE, 59);
		gcf.set(GregorianCalendar.SECOND, 59);
		gcf.set(GregorianCalendar.MILLISECOND, 999);
		return get(gci, gcf);
	}

	/**
	 * Crea un vector contenente tutti mancati funzionamenti nel range passato
	 * @param dataIniz data inizio ricerca
	 * @param dataFine data fine ricerca
	 * @return vector di mancati funzionamenti
	 * @throws SQLException
	 */
	@Override
	public List<MancatoFunzionamento> get(GregorianCalendar dataIniz, GregorianCalendar dataFine) throws SQLException{
		if(dataIniz==null)
			throw new NullPointerException("La data di inizio passata non puo' essere null");
		if(dataFine==null)
			throw new NullPointerException("La data di fine passata non puo' essere null");
		Vector<MancatoFunzionamento> vm=new Vector<MancatoFunzionamento>();
		try {
			ps=conn.getConn().prepareStatement("SELECT * FROM MancatiFunzionamenti WHERE Ora BETWEEN ? AND ? ORDER BY Ora;");
			ps.setLong(1, dataIniz.getTimeInMillis());
			ps.setLong(2, dataFine.getTimeInMillis());
			rs=ps.executeQuery();
			while(rs.next()){
				MancatoFunzionamento m=new MancatoFunzionamento();
				GregorianCalendar gc=new GregorianCalendar();
				gc.setTime(new Date(rs.getLong("Ora")));
				try {
					m.setOra(gc);
					m.setDurata(rs.getLong("Durata"));
					m.setErrore(rs.getString("Errore"));
					vm.add(m);
				} catch (DataFormatException e) {
					e.printStackTrace();
				}
			}
		} finally {
			rs.close();
			ps.close();
			rs=null;
			ps=null;
		}
		return vm;
	}

	/**
	 * Aggiunge un mancatoFunzionamento al database
	 * @param m mancatoFunzionamento da aggiungere
	 * @return true se riesce ad aggiungere il mancatoFunzionamento, false altrimenti
	 * @throws DataFormatException Misura passato non totalmente settato
	 * @throws SQLException
	 * @throws DataNotFoundException Il report associato non esiste
	 */
	public boolean put(MancatoFunzionamento m) throws DataFormatException, SQLException, DataNotFoundException{
		boolean flag=false;
		if(m==null)
			throw new NullPointerException("Il MancatoFunzionamento non puo' essere null");
		if(!m.impostato())
			throw new DataFormatException("Il MancatoFunzionamento passato non e' totalmente settato");
		if(validaOra(m)){
			try{
				ps=conn.getConn().prepareStatement("INSERT INTO Misure (Ora, Durata, Errore, ReportsID) " +
													"VALUES (?,?,?,?);");
				ps.setLong(1, m.getOra().getTimeInMillis());
				ps.setLong(2, m.getDurata());
				ps.setString(3, m.getErrore());
				ReportsDBDAO rd=new ReportsDBDAO(conn);
				ps.setInt(4, rd.getId(m.getOra()));
				if(ps.executeUpdate()>0)
					flag=true;
			} finally {
				ps.close();
				ps=null;
			}
		}
		return flag;
	}

	/**
	 * Controlla se l'orario passato e' un orario valido, ovvero, se non va in conflitto con nessuna misura associata al sensore gia' presente nel db
	 * @param m misura da aggiungere
	 * @param s sensore a cui associare la misura
	 * @return true se la misura non va in conflitto con misure gia' presenti nel db, false altrimenti
	 * @throws DataFormatException Misura o sensore passati non totalmente settati
	 * @throws SQLException
	 * @throws DataNotFoundException Non esistono il sensore o il report nel db
	 */
	boolean validaOra(Misura m) throws SQLException, DataFormatException{
		boolean flag=true;
		if(m==null)
			throw new NullPointerException("La misura passata non puo' essere null");
		if(!m.impostato())
			throw new DataFormatException("La misura passata non e' totalmente settata");
		try {
			ps=conn.getConn().prepareStatement("SELECT * FROM MancatiFunzionamenti " +
					"WHERE (Ora < ? AND ? < (Ora+Durata*1000)) OR " +
					"(Ora < ? AND ? < (Ora+Durata*1000)) OR " +
					"(? < Ora AND (Ora+Durata*1000) < ?);");
			ps.setLong(1, m.getOra().getTimeInMillis());
			ps.setLong(2, m.getOra().getTimeInMillis());
			ps.setLong(3, m.getOra().getTimeInMillis()+m.getDurata()*1000);
			ps.setLong(4, m.getOra().getTimeInMillis()+m.getDurata()*1000);
			ps.setLong(5, m.getOra().getTimeInMillis());
			ps.setLong(6, m.getOra().getTimeInMillis()+m.getDurata()*1000);
			rs=ps.executeQuery();
			if(rs.next()){
				flag=false;
			}
		} finally {
			rs.close();
			ps.close();
			rs=null;
			ps=null;
		}
		return flag;
	}

	/**
	 * Controlla se l'orario passato e' un orario valido, ovvero, se non va in conflitto con nessuna misura associata al sensore gia' presente nel db
	 * @param m misura da aggiungere
	 * @param s sensore a cui associare la misura
	 * @return true se la misura non va in conflitto con misure gia' presenti nel db, false altrimenti
	 * @throws DataFormatException MancatoFunzionamento passato non totalmente settato
	 * @throws SQLException
	 * @throws DataNotFoundException Non esiste il report nel db
	 */
	private boolean validaOra(MancatoFunzionamento m) throws SQLException, DataFormatException{
		boolean flag=true;
		if(m==null)
			throw new NullPointerException("Il mancato funzionamento passato non puo' essere null");
		if(!m.impostato())
			throw new DataFormatException("Il mancato funzionamento passato non e' totalmente settata");
		try {
			ps=conn.getConn().prepareStatement("SELECT * FROM MancatiFunzionamenti " +
					"WHERE ((Ora < ? AND ? < (Ora+Durata*1000)) OR " +
					"(Ora < ? AND ? < (Ora+Durata*1000)) OR " +
					"(? < Ora AND (Ora+Durata*1000) < ?));");
			ps.setLong(1, m.getOra().getTimeInMillis());
			ps.setLong(2, m.getOra().getTimeInMillis());
			ps.setLong(3, m.getOra().getTimeInMillis()+m.getDurata()*1000);
			ps.setLong(4, m.getOra().getTimeInMillis()+m.getDurata()*1000);
			ps.setLong(5, m.getOra().getTimeInMillis());
			ps.setLong(6, m.getOra().getTimeInMillis()+m.getDurata()*1000);
			rs=ps.executeQuery();
			if(rs.next()){
				flag=false;
			}
		} finally {
			rs.close();
			ps.close();
			rs=null;
			ps=null;
		}
		return flag;
	}
}